Skip to content

Conversation

jjhembd
Copy link
Contributor

@jjhembd jjhembd commented Sep 29, 2025

Description

This PR reworks the voxel shaders to perform the raymarching in eye-space coordinates, rather than model-space. This avoids precision problems when zoomed in close to small samples in a large model space.

Most of the precision problem came from the sampling of the voxel. The traversal in Octree.glsl has been re-worked to input a two-part coordinate:

  • Tile coordinate: the integer [x, y, z, w] indices of the tile within the tileset
  • Tile UV coordinate: floating point numbers in the interval [0, 1] indicating the x, y, z positions within the tile

This new input to the traversal effectively gives us nearly double precision when sampling a large dataset.

Some parts of the code are still in a local model-centered coordinate system:

  • Calculation of the "jacobian" (partial derivatives of the transform from Cartesian to cylindrical/ellipsoidal coordinates). This matrix is more easily defined in model coordinates.
  • Clipping plane intersections
  • Many of the initial shape intersection routines

The latter 2 items could also be converted to eye coordinates with additional work. I did not do that work in this PR, mostly because of time constraints. I also haven't observed significant precision issues with those intersections, so that work does not seem urgent at this time.

Issue number and link

Fixes #12061

Testing plan

  1. Load the testing Sandcastle and verify sharp rendering. Compare to the same Sandcastle in main.
  2. Load the existing Voxel Sandcastles and verify that all shape types work, and picking works

Author checklist

  • I have submitted a Contributor License Agreement
  • I have added my name to CONTRIBUTORS.md
  • I have updated CHANGES.md with a short summary of my change
  • I have added or updated unit tests to ensure consistent code coverage
  • I have updated the inline documentation, and included code examples where relevant
  • I have performed a self-review of my code

Jeshurun Hembd added 29 commits July 29, 2025 09:48
Copy link

Thank you for the pull request, @jjhembd!

✅ We can confirm we have a CLA on file for you.

float clipAmount;
float pixelWidth = czm_metersPerPixel(position);
bool breakAndDiscard = false;
for (int i = 0; i < ${clippingPlanesLength}; ++i)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for cleaning this up! Should this (either here or a later PR) be adjusted to use compiler define statements?

Comment on lines 50 to 65
/**
* An event triggered when a new clipping plane is added to the collection. Event handlers
* are passed the new plane and the index at which it was added.
* @type {Event}
* @default Event()
*/
this.planeAdded = new Event();

/**
* An event triggered when a new clipping plane is removed from the collection. Event handlers
* are passed the new plane and the index from which it was removed.
* @type {Event}
* @default Event()
*/
this.planeRemoved = new Event();

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think these should both be readonly, and therefore the default should be extraneous.

Suggested change
/**
* An event triggered when a new clipping plane is added to the collection. Event handlers
* are passed the new plane and the index at which it was added.
* @type {Event}
* @default Event()
*/
this.planeAdded = new Event();
/**
* An event triggered when a new clipping plane is removed from the collection. Event handlers
* are passed the new plane and the index from which it was removed.
* @type {Event}
* @default Event()
*/
this.planeRemoved = new Event();
/**
* An event triggered when a new clipping plane is added to the collection. Event handlers
* are passed the new plane and the index at which it was added.
* @type {Event}
* @readonly
*/
this.planeAdded = new Event();
/**
* An event triggered when a new clipping plane is removed from the collection. Event handlers
* are passed the new plane and the index from which it was removed.
* @type {Event}
* @readonly
*/
this.planeRemoved = new Event();

Copy link
Contributor

@ggetz ggetz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @jjhembd! Overall, this is good shape. The code is looking pretty clean to me. I didn't look deeply into the math itself, but the results are looking great!

Finally a reminder to update CHANGES.md.

}

vec3 scaleShapeUvToShapeSpace(in vec3 shapeUv) {
// TODO: scaling is wrong!
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you mentioned offline that this is TODO is on your radar–A reminder to address here or to open an issue.

vec3 rtu = u_cylinderEcToRadialTangentUp * positionEC;
// 2. Compute change in angular and radial coordinates. TODO: scaling camera is wrong!
vec2 dPolar = computePolarChange(rtu.xy, u_cameraShapePosition.x * u_cylinderWorldToLocalScale.x);
// TODO: this is wrong! Scaling needs to be applied in local XYZ coordinates
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here.

@jjhembd jjhembd marked this pull request as ready for review October 1, 2025 14:16
@lukemckinstry
Copy link
Contributor

I did some basic testing on existing voxel sandcastles and everything I observed matches main 👍 @jjhembd

@lukemckinstry lukemckinstry self-requested a review October 1, 2025 18:55
@jjhembd jjhembd added this pull request to the merge queue Oct 1, 2025
Merged via the queue into main with commit 8d67e9e Oct 1, 2025
9 checks passed
@jjhembd jjhembd deleted the voxel-rte branch October 1, 2025 19:00
@ggetz
Copy link
Contributor

ggetz commented Oct 7, 2025

@jjhembd I'm now noticing a failing test in main when running the tests locally. Is this on your radar?

1) picks a voxel cell from a VoxelPrimitive
     Scene/Pick pickVoxel
     RuntimeError: Fragment shader failed to compile.  Compile log: ERROR: 0:89: '<<' : bit-wise operator supported in GLSL ES 3.00 and above only
ERROR: 0:174: '>>' : bit-wise operator supported in GLSL ES 3.00 and above only
ERROR: 0:177: '&' : bit-wise operator supported in GLSL ES 3.00 and above only
ERROR: 0:219: '>>' : bit-wise operator supported in GLSL ES 3.00 and above only
ERROR: 0:690: '<<' : bit-wise operator supported in GLSL ES 3.00 and above only
ERROR: 0:697: 'max' : no matching overloaded function found
ERROR: 0:697: '<<' : bit-wise operator supported in GLSL ES 3.00 and above only
ERROR: 0:697: 'min' : no matching overloaded function found
ERROR: 0:697: 'assign' : cannot convert from 'const mediump float' to 'highp 3-component vector of int'

error properties: undefined: undefined
Error
    at new RuntimeError (packages/engine/Source/Core/RuntimeError.js:38:11 <- Build/CesiumUnminified/Cesium.js:11184:13)
    at createAndLinkProgram (packages/engine/Source/Renderer/ShaderProgram.js:266:9 <- Build/CesiumUnminified/Cesium.js:26828:11)
    at reinitialize (packages/engine/Source/Renderer/ShaderProgram.js:470:19 <- Build/CesiumUnminified/Cesium.js:26981:21)
    at initialize2 (packages/engine/Source/Renderer/ShaderProgram.js:463:3 <- Build/CesiumUnminified/Cesium.js:26976:5)
    at ShaderProgram._bind (packages/engine/Source/Renderer/ShaderProgram.js:542:3 <- Build/CesiumUnminified/Cesium.js:27035:5)
    at beginDraw (packages/engine/Source/Renderer/Context.js:1304:17 <- Build/CesiumUnminified/Cesium.js:41212:19)
    at Context.draw (packages/engine/Source/Renderer/Context.js:1414:3 <- Build/CesiumUnminified/Cesium.js:41300:5)
    at DrawCommand.execute (packages/engine/Source/Renderer/DrawCommand.js:642:11 <- Build/CesiumUnminified/Cesium.js:23965:13)
    at executeCommand (packages/engine/Source/Scene/Scene.js:2281:13 <- Build/CesiumUnminified/Cesium.js:229053:15)
    at performVoxelsPass (packages/engine/Source/Scene/Scene.js:2407:5 <- Build/CesiumUnminified/Cesium.js:229120:7)

@jjhembd
Copy link
Contributor Author

jjhembd commented Oct 7, 2025

@ggetz Yes, thanks for the reminder. I need to turn off the voxel picking specs in WebGL1 contexts. See #12956

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Relative-to-eye rendering for voxels

3 participants